package nz.ac.massey.doi_information_extractor.main;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Authenticator;
import java.sql.SQLException;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileSystemView;

import nz.ac.massey.doi_information_extractor.data.Author;
import nz.ac.massey.doi_information_extractor.data.Publication;
import nz.ac.massey.doi_information_extractor.data.Settings;
import nz.ac.massey.doi_information_extractor.database.DatabaseImplementation;
import nz.ac.massey.doi_information_extractor.database.DatabaseInterface;
import nz.ac.massey.doi_information_extractor.database.MockDatabase;
import nz.ac.massey.doi_information_extractor.gui.*;
import nz.ac.massey.doi_information_extractor.output.OutputException;
import nz.ac.massey.doi_information_extractor.output.PDFOutput;
import nz.ac.massey.doi_information_extractor.output.PDFOutputInterface;
import nz.ac.massey.doi_information_extractor.parser.ParserEngine;
import nz.ac.massey.doi_information_extractor.parser.ParserEngineInterface;
import nz.ac.massey.doi_information_extractor.parser.PublicationNotFoundException;

/**
 * Main class for doi_information_extractor, handles events from the GUI and dispatches jobs
 * to the database, output and parser engines.
 */
public class Main{

	private static Mp3Player player;
	
	// User Interface
	private AbstractGui userInterface;

	// File chooser for save file dialog
	private DefaultNameFileChooser saveFileDialog;

	// Database controller
	private DatabaseInterface database;

	// Settings
	private Settings currentSettings;

	public static void main(String[] args) {
		new Main();
	}

	/**
	 * Constructor for main program class
	 */
	public Main(){
		userInterface = new Gui(this);

		saveFileDialog = new DefaultNameFileChooser("");
		saveFileDialog.setDialogTitle("Save Publication As");
		saveFileDialog.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

		PdfFileFilter pdfFileFilter = new PdfFileFilter();
		saveFileDialog.setFileFilter(pdfFileFilter);
		loadSettings();
		player = new Mp3Player();
		player.start();
	}

	public void deletePublication(){
		try {
			database.delete(userInterface.getDoi());
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(userInterface, "Error removing publicationg from database");
		}
		userInterface.setPublication(null);
	}

	/**
	 * Performs the processing for a DOI,
	 *
	 * @param doi The DOI to search for
	 */
	public void processDoi() {
		promptSaveOpenPublication();

		String doi = DoiDialog.showDialog(userInterface);

		if( doi == null || doi.equals(""))
			return;

		ProgressbarUpdater pgu = new ProgressbarUpdater(userInterface.getProgressBar());
		pgu.start();

		ParserEngineInterface parserEngine = new ParserEngine();
		Publication result;

		try {
			result = parserEngine.getInformationFromDOI(doi);
		} catch (PublicationNotFoundException e) {
			pgu.stopUpdater();
			JOptionPane.showMessageDialog(userInterface,"Publication not found.");
			return;
		} catch (IOException e) {
			pgu.stopUpdater();
			JOptionPane.showMessageDialog(userInterface,"Cannot connect to internet.");
			return;
		}catch(Exception e){
			pgu.stopUpdater();
			JOptionPane.showMessageDialog(userInterface,"Error: " + e.getMessage());
			return;
		}
		pgu.stopUpdater();
		userInterface.setPublication(result);
	}

	private void promptSaveOpenPublication(){
		if (userInterface.getPublication() != null) {
			int val =  JOptionPane.showConfirmDialog(userInterface, "Save open publication?", "Save", JOptionPane.YES_NO_OPTION);
			if (val == JOptionPane.OK_OPTION) {
				savePublication();
				userInterface.setPublication(null);
			}
		}
	}

	/**
	 * Persists a publication, adds a new entry for publication or
	 * creates one if already exists
	 * @param publication The publication to save to disk
	 */
	public void savePublication(){
		Publication publication = userInterface.getPublication();
		if(publication == null)
			return;
		try {
			database.write(publication);
		} catch (SQLException e) {
			JOptionPane.showMessageDialog(userInterface,"Error saving to database");
		}
	}

	/**
	 * Outputs a a publication to PDF using JFileChooser and the output engine
	 * @param publication The publication to output to pdf
	 */
	public void outputPublication(){
		Publication publication = userInterface.getPublication();
		saveFileDialog.setDefaultName(publication.getRimsId());
        if (saveFileDialog.showSaveDialog(userInterface) == JFileChooser.APPROVE_OPTION) {

        	// Get the file from the result of the file chooser
            File file = saveFileDialog.getSelectedFile();

            try{
				// Add .pdf to end of path if not already there
				String path = file.getAbsolutePath();
				if(!path.endsWith(".pdf"))
					path += ".pdf";

				PDFOutputInterface outputEngine = new PDFOutput();
				outputEngine.outputToPdf(publication, path);
            }catch(OutputException io){
    			JOptionPane.showMessageDialog(userInterface,io.getMessage());
            }
        }
	}

	/**
	 * Opens an already created publication and presents it on the GUI
	 */
	public void openPublication(){
		promptSaveOpenPublication();
		Publication selectedPublication = OpenPublicationDialog.showDialog(userInterface, database);
		if(selectedPublication != null){
			userInterface.setPublication(selectedPublication);
		}
	}

	/**
	 * Displays a dialog to the use to update the settings for the program
	 * @throws ClassNotFoundException
	 * @throws SQLException
	 */
	public void updateSettings(){
		Settings newSettings = SettingsDialog.showDialog(userInterface, currentSettings);

		if(newSettings == null)
			return;

		currentSettings = newSettings;
		saveSettings();
		applySettings();
	}

	/**
	 * Displays the prompt to set the selected author
	 */
	public void setAuthor(){
		Author inputAuthor = userInterface.getSelectedAuthor();

		if(inputAuthor == null)
			return;

		Author outputAuthor = SelectAuthorDialog.showDialog(userInterface,inputAuthor,database);

		if(outputAuthor != null){
			userInterface.setSelectedAuthor(outputAuthor);
		}
	}

	/**
	 * Saves the current settings to disk
	 */
	private void saveSettings(){
		FileOutputStream outputStream = null;
		ObjectOutputStream objectOutputStream = null;

		try{
			outputStream = new FileOutputStream(getDocumentsFolder() + "/doi ie.settings");
			objectOutputStream = new ObjectOutputStream(outputStream);
			objectOutputStream.writeObject(currentSettings);
			outputStream.close();
		}catch(IOException ex){
			JOptionPane.showMessageDialog(userInterface, "There was an error saving your settings to disk.\nYour current settings may not be loaded the next time you run the program.");
		}
	}
	/**
	 * Loads the settings from disk, if the settings file is not found default settings are used
	 */
	private void loadSettings(){
		FileInputStream fis = null;
		ObjectInputStream ois = null;

		try {
			fis = new FileInputStream(getDocumentsFolder() + "/doi ie.settings");
			ois = new ObjectInputStream(fis);
			Settings settings = (Settings)ois.readObject();
			if(settings != null)
				currentSettings = settings;
			else
				currentSettings = new Settings();
		} catch (Exception e) {
			currentSettings = new Settings();
			JOptionPane.showMessageDialog(userInterface, e.getMessage());
		}
		applySettings();
	}

	/**
	 * Applies the current settings
	 */
	private void applySettings(){
		if(!currentSettings.getProxy().equals("")){
			System.setProperty("http.proxyHost", currentSettings.getProxy());
			System.setProperty("http.proxyPort", Integer.toString(currentSettings.getPort()));
		}
		if(!currentSettings.getUserId().equals("") && !currentSettings.getUserPassword().equals("")) {
			MasseyProxyAuthenticator proxyAuth = new MasseyProxyAuthenticator(currentSettings.getUserId(), currentSettings.getUserPassword());
			Authenticator.setDefault(proxyAuth);
		}

		if(!currentSettings.getDatabasePath().equals(""))
		{
			String path = currentSettings.getDatabasePath();
			if(!path.endsWith(".db"))
			{
				path += ".db";
			}
			try {
				database = new DatabaseImplementation(path);
			} catch (Exception e) {
				database = new MockDatabase();
				JOptionPane.showMessageDialog(userInterface, "Error creating database. Using non-persistent database.");
			}
		}
		else{
			JFileChooser dbFileChooser = new JFileChooser();

			dbFileChooser.setDialogTitle("Select Database");
			dbFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
			dbFileChooser.removeChoosableFileFilter(dbFileChooser.getFileFilter());
			dbFileChooser.setFileFilter(new DbFileFilter());

			if(dbFileChooser.showOpenDialog(userInterface) == JFileChooser.APPROVE_OPTION){
				String path = dbFileChooser.getSelectedFile().getAbsolutePath();

				if(!path.endsWith(".db"))
				{
					path += ".db";
				}
	            try {
					database = new DatabaseImplementation(path);
				} catch (Exception e) {
					database = new MockDatabase();
					JOptionPane.showMessageDialog(userInterface, "Error creating database. Using non-persistent database.");
					return;
				}
	            currentSettings.setDatabasePath(path);
			}else{
				database = new MockDatabase();
	            currentSettings.setDatabasePath("Non-persistent");
	            JOptionPane.showMessageDialog(userInterface, "No database selected, using non-persistent database.");
			}
		}
	}

	/**
	 * Terminates the program
	 */
	public void exit(){
		promptSaveOpenPublication();
		saveSettings();
		database.closeConnection();
		userInterface.setVisible(false);
		userInterface.dispose();
		System.exit(0);
	}

	private String getDocumentsFolder(){
	     FileSystemView fw = saveFileDialog.getFileSystemView();
	     return fw.getDefaultDirectory().getAbsolutePath();
	}

}
